# -*- coding: utf-8 -*-
import urllib, urllib2, json
from decimal import Decimal
import logging
import random
from datetime import date

from django.db import models
from django.contrib.auth.models import User
from django.utils.encoding import smart_str
from django.db.models import Q
from django.conf import settings
from django.utils.safestring import mark_safe
from django.utils.html import strip_tags

from tagging.fields import TagField
from tagging.models import TaggedItem, Tag

from brispon.util.tweeter import tweet
from brispon.util.email import send_email
from brispon.util.sms import send
from brispon import brispon_setting as bs

SITE_ADDRESS = getattr(bs, 'SITE_ADDRESS', 'http://localhost')

class SiteUser(models.Model):
    user = models.OneToOneField(User)
    mobile = models.CharField(max_length=20)
    
    def __unicode__(self):
        return self.user.username
    
    def save(self, *args, **kwargs):
        super(SiteUser, self).save(*args, **kwargs)
    
User.profile = property(lambda u: SiteUser.objects.get_or_create(user=u)[0])

class Category(models.Model):
    name = models.CharField(max_length=50)
    icon = models.ImageField(upload_to='image/%Y/%m/%d')
    
    class Meta:
        ordering = ['name']
        
    def __unicode__(self):    
        return self.name
    
class Order(models.Model):    
    name = models.CharField(max_length=50, unique=True)
    store = models.ForeignKey('Store', null=True, blank=True)
    event = models.ForeignKey('Event', null=True, blank=True)
    tel = models.CharField('Phone', max_length=50, null=True, blank=True)
    mobile = models.CharField('Mobile Phone', max_length=50, null=True, blank=True)
    email = models.EmailField(null=True, blank=True)
    price = models.SmallIntegerField(default=50)
    note = models.TextField(max_length=1000, blank=True)
    registry = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['registry']
        
    def __unicode__(self):
        return self.name

# 0.1 : 11.1km from http://en.wikipedia.org/wiki/Decimal_degrees    
NEAR_DISTANCE = getattr(bs, 'NEAR_DISTANCE', 0.03)

def get_geo(addr):
    GOOGLE_MAP_API = getattr(bs, 'MAP_API_URL', 'http://maps.googleapis.com/maps/api/geocode/json?address=')
    latitude = None
    longitude = None
    try:
        url_address = '%s%s%s' % (GOOGLE_MAP_API, 
                                  urllib.quote_plus(smart_str(addr)), 
                                  '&sensor=false')
        response = urllib2.urlopen(url_address).read()
        result = json.loads(response)
        if result and result['status'] == 'OK':
            latitude = Decimal(str(result['results'][0]['geometry']['location']['lat']))
            longitude = Decimal(str(result['results'][0]['geometry']['location']['lng']))
        return latitude, longitude
    except:
        return ''

class BaseModel(models.Model):
    name = models.CharField(max_length=50)
    representative = models.TextField(help_text='모바일 255자내외 대표적인 문구를 넣어주세요',
                            max_length=255, blank=True)
    description = models.TextField(help_text='500자내외 세부적인 정보를 넣어주세요',
                             max_length=500, blank=True)
    extra = models.CharField(help_text='검색용 정보를 넣어주세요(화면출력되지 않음)',
                             max_length=100, blank=True)
    thumbnail = models.ImageField(help_text='간략한 광고 이미지를 넣어주세요(270*200)',
                                  upload_to='image/%Y/%m/%d')
    image1 = models.ImageField(help_text='이미지 파일을 넣어주세요(700*400)', 
                                    upload_to='image/%Y/%m/%d', blank=True)
    image2 = models.ImageField(help_text='이미지 파일을 넣어주세요(700*400)', 
                                    upload_to='image/%Y/%m/%d', blank=True)
    image3 = models.ImageField(help_text='이미지 파일을 넣어주세요(700*400)', 
                                    upload_to='image/%Y/%m/%d', blank=True)
    image4 = models.ImageField(help_text='이미지 파일을 넣어주세요(700*400)', 
                                    upload_to='image/%Y/%m/%d', blank=True)
    image5 = models.ImageField(help_text='이미지 파일을 넣어주세요(700*400)', 
                                    upload_to='image/%Y/%m/%d', blank=True)
    image6 = models.ImageField(help_text='이미지 파일을 넣어주세요(700*400)', 
                                    upload_to='image/%Y/%m/%d', blank=True)
    image7 = models.ImageField(help_text='이미지 파일을 넣어주세요(700*400)', 
                                    upload_to='image/%Y/%m/%d', blank=True)
    image8 = models.ImageField(help_text='이미지 파일을 넣어주세요(700*400)', 
                                    upload_to='image/%Y/%m/%d', blank=True)
    image9 = models.ImageField(help_text='이미지 파일을 넣어주세요(700*400)', 
                                    upload_to='image/%Y/%m/%d', blank=True)
    image10 = models.ImageField(help_text='이미지 파일을 넣어주세요(700*400)', 
                                    upload_to='image/%Y/%m/%d', blank=True)
    
    tags = TagField(help_text='주요정보를 넣어주세요, 주요정보가 자동으로 입력됩니다',)
    
    # basic methods for what tagging provides
    def set_tags(self):
        '''Tags is a space or comma-delimited string'''
        Tag.objects.update_tags(self, self.tags)

    def get_tags(self):
        return Tag.objects.get_for_object(self)

    # extra
    tweet = models.CharField(max_length=140, blank=True)
    searchable = models.TextField(help_text='검색을 위해 사용합니다',
                                  max_length=1000, blank=True)
    registry = models.DateTimeField(auto_now_add=True)
    
    def save(self, *args, **kwargs):
        super(BaseModel, self).save(*args, **kwargs)
        self.set_tags()

class StoreManager(models.Manager):
    def get_promotion(self):
        return Store.objects.filter(Q(ad_type='PROMOTION'))

    def get_having_address(self):
        return Store.objects.exclude(Q(addr__isnull=True)|Q(addr__exact=''))
    
    def get_near_stores_by_addr(self, addr):
        latitude, longitude = get_geo(addr)
        
        if latitude and longitude:
            latitude = float(longitude)
            longitude = float(longitude)
            return Store.objects.filter(Q(latitude__gt=latitude-NEAR_DISTANCE), 
                                 Q(latitude__lt=latitude+NEAR_DISTANCE),
                                 Q(longitude__gt=longitude-NEAR_DISTANCE),
                                 Q(longitude__lt=longitude+NEAR_DISTANCE))
            
        else:
            return None
        
    def get_near_stores_by_coordi(self, latitude, longitude):
        return Store.objects.filter(Q(latitude__gt=latitude-NEAR_DISTANCE), 
                             Q(latitude__lt=latitude+NEAR_DISTANCE),
                             Q(longitude__gt=longitude-NEAR_DISTANCE),
                             Q(longitude__lt=longitude+NEAR_DISTANCE))
            
class Store(BaseModel):
    BASIC = 'BASIC'
    PROMOTION = 'PROMOTION'
    AD_TYPE = (
        (BASIC, u'basic'),
        (PROMOTION, u'promotion'),
        )
    HEAD = 'HEAD'
    BRANCH = 'BRANCE'
    TEST = 'TEST'
    STORE_TYPE = (
            (HEAD, u'head'),
            (BRANCH, u'branch'),
            (TEST, u'test')
            )
    enname = models.CharField(help_text='영어이름을 넣어주세요', max_length=50, blank=True)
    category = models.ForeignKey(Category)
    ad_type = models.CharField(max_length=10, choices=AD_TYPE, default=BASIC)
    store_type = models.CharField(max_length=10, choices=STORE_TYPE, default=HEAD)
    logo = models.ImageField(help_text='store의 logo 이미지 파일을 넣어주세요', 
                             upload_to='image/%Y/%m/%d', blank=True)
    
    detail_addr = models.CharField('Detail Address',
                                   help_text='층, 샵 세부 주소를 넣어주세요',
                                   max_length=50, blank=True)
    addr = models.CharField('Address',
                            help_text='지도에 출력될 주소를 넣어주세요',
                            max_length=255, blank=True)
    tel1 = models.CharField('Local Phone1', max_length=50, blank=True)
    tel2 = models.CharField('Local Phone2', max_length=50, blank=True)
    internet_tel = models.CharField('Internet Phone', max_length=50, blank=True)
    mobile1 = models.CharField('Moblie Phone1', max_length=50, blank=True)
    mobile2 = models.CharField('Moblie Phone2', max_length=50, blank=True)
    fax = models.CharField(max_length=50, blank=True)
    kakao = models.CharField(max_length=50, blank=True)
    email = models.EmailField(blank=True)
    website = models.URLField('Website Address', max_length=255, blank=True)
    trading_hours = models.TextField(help_text='영업시간을 넣어주세요',
                             max_length=100, blank=True)
    branchs = models.ManyToManyField('self', null=True, blank=True)
    like = models.IntegerField(default=0)
    latitude = models.FloatField(default=0.0)
    longitude = models.FloatField(default=0.0)
    
    objects = models.Manager()
    search_objects = StoreManager()

    class Meta:
        ordering = ['name'] #random

    def __unicode__(self):
        return self.name
    
    def save(self, *args, **kwargs):
#        if self.latitude == 0.0  and self.longitude == 0.0:
#            latitude, longitude = get_geo(self.addr)
#            if latitude and longitude:
#                self.latitude = Decimal(latitude)
#                self.longitude = Decimal(longitude)
#            else:
#                self.latitude = Decimal('-25.35')
#                self.longitude = Decimal('131.0')
            
        self.searchable = ''    
        self.searchable = self.name + ' ' + \
                            self.addr + ' ' + \
                            strip_tags(self.description) + ' ' + \
                            strip_tags(self.extra) + ' ' + \
                            self.category.name + ' ' + \
                            self.tags
#        if self.tweet:
#            tweet(self.tweet + ' ' + self.tweet_url())
        super(Store, self).save(*args, **kwargs)

    @models.permalink
    def url(self):
        return ('brispon.views.store.detail', [self.name])

    @models.permalink
    def murl(self):
        return ('brispon.views.store.mdetail', [self.name])

    def tweet_url(self):
        return SITE_ADDRESS + self.url()
    
    def get_events(self):
        ## show only 3-5 events
        return [event for event in self.event_set.all()]
    
    def get_aevents(self):
        return [event for event in self.event_set.all() if event.active]
    
    def get_aevents_count(self):
        return len([event for event in self.event_set.all() if event.active])
    
    def get_aevents_as_html(self):
        hs =''
        for event in self.event_set.all():
            if event.active:
                hs += '<li><a href="%s">%s</a></li>' % (event.url(), event.name)
        if hs:
            return mark_safe("""<div class="pop"><ul>%s</ul></div>""" % hs)
        return ''

    def get_acoupons(self):
        coupons = []
        for event in self.event_set.all():
            if event.active and event.coupon():
                coupons.append(event.coupon)
        return coupons

    def get_coupons(self):
        coupons = []
        for event in self.event_set.all():
            coupons.append(event.coupon)
        return coupons

    def get_similar_stores(self):
        stores_by_tag = self.get_similar_stores_by_tag()
#        stores_by_category = self.get_similar_stores_by_category()
#        return set(list(stores_by_tag) + list(stores_by_category))
        return set(list(stores_by_tag))
    
    def get_similar_stores_by_tag(self):
        return TaggedItem.objects.get_union_by_model(Store, self.tags.split(' ')).exclude(id=self.id)[:5]
    
    def get_similar_stores_by_category(self):
        return Store.objects.filter(category=self.category).exclude(id=self.id).order_by('-like')[:5]
    
    def get_near_stores(self):
        return Store.objects.filter(Q(latitude__gt=self.latitude-NEAR_DISTANCE), 
                                 Q(latitude__lt=self.latitude+NEAR_DISTANCE),
                                 Q(longitude__gt=self.longitude-NEAR_DISTANCE),
                                 Q(longitude__lt=self.longitude+NEAR_DISTANCE)).exclude(id=self.id)

    def liked_by(self, user):
        if user.is_anonymous():
            return False
        fs = FavoriteStore.objects.filter(user=user, store=self)
        if fs:
            fs.delete()
            if self.like > 0:
                self.like = self.like - 1
        else:
            FavoriteStore(user=user, store=self).save()
            self.like = self.like + 1
        self.save()
        return True
        
    def is_favorite(self, user):
        if not user.is_anonymous():
            fs = FavoriteStore.objects.filter(user=user, store=self)
            return True if fs else False
        return False
    
class FavoriteStore(models.Model):
    user = models.ForeignKey(User)
    store = models.ForeignKey(Store)
    registry = models.DateTimeField(auto_now_add=True)

class EventManager(models.Manager):
    def get_aevents(self):
        return Event.objects.filter(active=True)

    def get_aevents_without_mevents(self):
        return Event.objects.filter(active=True, event_type=Event.BASIC)
    
    def get_mevents(self):
        return Event.objects.filter(event_type=Event.MAIN, active=True)

    def get_sevents(self):
        return Event.objects.filter(event_type=Event.SIDE, active=True)
    
    def change_active(self):
        events = Event.objects.filter(active=True)
        for event in events:
            if event.edate < date.today():
                event.active = False
                event.save()

class Event(BaseModel):
    BASIC = 'BASIC'
    MAIN = 'MAIN'
    SIDE = 'SIDE'
    TEST = 'TEST'
    EVENT_TYPE = (
        (BASIC, u'basic'),
        (MAIN, u'main'),
        (SIDE, u'side'),
        (TEST, u'test'),
        )
    short_description = models.TextField(help_text='255자내외 간략한 정보를 넣어주세요',
                             max_length=255, blank=True)
    store = models.ForeignKey(Store, blank=True)
    event_type = models.CharField(max_length=10, choices=EVENT_TYPE, default=BASIC)
    sdate = models.DateField()
    edate = models.DateField()
    active = models.BooleanField(default=True)
    detail = models.ImageField(help_text='event의 자세한 광고를 넣어주세요',
                                    upload_to='image/%Y/%m/%d', blank=True)
    mainimage = models.ImageField(help_text='event의 main 광고를 넣어주세요(955*390)',
                                    upload_to='image/%Y/%m/%d', blank=True)
    sideimage = models.ImageField(help_text='event의 side 광고를 넣어주세요(135*200)',
                                    upload_to='image/%Y/%m/%d', blank=True)
    
    objects = EventManager()
    
    class Meta:
        ordering = ['edate']

    def __unicode__(self):
        return '%s %s' % (self.store.name, self.name)
    
    def save(self, *args, **kwargs):
        today = date.today()
        if self.sdate <= today and today < self.edate:
            self.active = True
        else:
            self.active = False
            
        self.searchable = ''
        self.searchable = self.store.name + ' ' + \
                            strip_tags(self.store.description) + ' ' + \
                            strip_tags(self.store.extra) + ' ' + \
                            self.store.addr + ' ' + \
                            self.store.tags + ' ' + \
                            self.name + ' ' + \
                            strip_tags(self.description) + ' ' + \
                            strip_tags(self.extra) + ' ' + \
                            self.tags

        self.tags += self.store.tags
        
        self.store.searchable += self.name + ' ' + \
                                    strip_tags(self.description) + ' ' + \
                                    strip_tags(self.extra) + ' ' + \
                                    self.tags
        self.store.save()
        
        if self.tweet:
            tweet(self.tweet + ' ' + self.tweet_url())
            
        super(Event, self).save(*args, **kwargs)
        
    @models.permalink
    def url(self):
#        return ('brispon.views.event.detail', [self.name])
        return ('brispon.views.event.detail', [self.id])
    
    @models.permalink
    def murl(self):
#        return ('brispon.views.event.mdetail', [self.name])
        return ('brispon.views.event.mdetail', [self.id])

    @models.permalink
    def privew(self):
        return ('brispon.views.event.privew', [str(self.id)])

    def tweet_url(self):
        return SITE_ADDRESS + self.url()
    
    def get_date(self):
        return self.sdate.strftime('%d %b %Y') + '-' + self.edate.strftime('%d %b %Y') 
    
    def get_day(self):
        return self.sdate.strftime('%a, %d %b') + '-' + self.edate.strftime('%a, %d %b') 
    
    def get_coupon_status(self):
        if not self.coupon:
            return None
        if not self.active:
            return 'PAST'
        return self.coupon.get_status()
    
    def get_similar_events(self):
        return TaggedItem.objects.get_union_by_model(Event, self.tags.split(' ')).exclude(id=self.id)[:5]
    
    def get_similar_stores(self):
        return self.store.get_similar_stores()

class FavoriteCoupon(models.Model):
    user = models.ForeignKey(User)
    coupon = models.ForeignKey('Coupon')
    registry = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        ordering = ['registry'] 
    
class CouponManager(models.Manager):
    def get_acoupon(self):
        return Coupon.objects.all()

class Coupon(models.Model):
    name = models.CharField(max_length=150, blank=True)
    event = models.OneToOneField(Event, null=True, blank=True)
    keys = models.TextField(max_length=1000, blank=True)
    start_count = models.IntegerField(default=bs.DEFAULT_COUPON)
    current_count = models.IntegerField(default=bs.DEFAULT_COUPON)
    textmsg = models.CharField(max_length=150, null=True, blank=True)
    registry = models.DateTimeField(auto_now_add=True)
    objects = CouponManager()

    class Meta:
        ordering = ['registry']

    def __unicode__(self):
        return self.name

    @models.permalink
    def url(self):
        return ('brispon.views.coupon.detail', [str(self.id)])

    @models.permalink
    def murl(self):
        return ('brispon.views.coupon.mdetail', [str(self.id)])
    
    def save(self, *args, **kwargs):
        if not self.keys:
            self.keys = ' '.join([str(i+1) + str('.') + str(random.randint(10000, 99999)) for i in range(self.start_count)])
        super(Coupon, self).save(*args, **kwargs)
    
    def display_keys(self):
        return '<ol>%s</ol>' % ''.join(['<li>%s</li>'%key for key in self.keys.split(' ')])
    
    def get_key(self, index):
        return index and self.keys.split(' ')[index] or self.keys.split(' ')[0] 
    
    def send_keys_by_email(self):
        if self.order:
            to = self.order.email
            send_email(to, 'keys', self.display_keys())
            info('Sending keys : %s - %s' % (self.name, to))
        else:
            error('Fail to send keys. There is no email address : %s ' % self.name)
        
    def available(self):
        return True if self.current_count > 0 else False
    
    def check_limited_aday(self, user):
        fc = FavoriteCoupon.objects.filter(user=user)
        today = date.today()
        todayfc = fc.filter(registry=today)
        limited_aday = getattr(bs, 'LIMITED_COUPON', 3)
        if len(todayfc) > limited_aday:
            return True
        return False
    
    def get_status(self):
        return '%s/%s' % (self.current_count, self.start_count)
    
    def count_down(self):
        if self.current_count > 1:
            self.current_count = self.current_count - 1
            self.save()

    def get_current_percent(self):
        return self.start_count / self.current_count * 100

    def owned_by(self, user):
        fs = FavoriteCoupon.objects.filter(user=user, coupon=self)
        if fs:
            return 'had'
        else:
            FavoriteCoupon(user=user, coupon=self).save()
            self.count_down()
            return 'sent'
    
    def check_user(self, user):
        if user.is_staff:
            return True 
        fs = FavoriteCoupon.objects.filter(user=user, coupon=self)      
        return False if fs else True
    
    def get_msg(self):
        msg = '%s : %s - %s' % \
                (self.textmsg, 
                 self.get_key(self.start_count-self.current_count),
                 SITE_ADDRESS + self.event.url())
        return msg
        
    def send_coupon_by_sms(self, user):
        if not self.available():
            return False, 'No coupons'
        if not self.check_user(user):
            return False, 'You have got this coupon before' # you've got coupon before
        if self.check_limited_aday(user):
            limited_aday = getattr(bs, 'LIMITED_COUPON', 3)
            return False, 'Get only %d coupons a day' % limited_aday
        try:
            msg = self.get_msg()
            to = '61' + user.profile.mobile[1:]
            send(to, msg)
            FavoriteCoupon(user=user, coupon=self).save()
            self.count_down()
        except:
            return False, 'Sending SMS is something wrong' # something wrong
        return True, 'You will get this coupon by SMS in 1 min' # success
        
class Recruit(models.Model):
    name = models.CharField(max_length=50)
    store = models.ForeignKey(Store, null=True, blank=True)
    description = models.TextField(help_text='구인 세부정보를 넣어주세요',
                             max_length=500, blank=True)
    edate = models.DateField(null=True, blank=True)
    registry = models.DateTimeField(auto_now_add=True) 

    class Meta:
        ordering = ['registry']

    def __unicode__(self):
        return self.name
    
    @models.permalink
    def murl(self):
        return ('brispon.views.recruit.mdetail', [str(self.id)])

def info(msg):
    Logging.objects.create_info(msg)
    
def error(msg):
    Logging.objects.create_error(msg)
        
class LoggingManager(models.Manager):
    def create_info(self, message):
        Logging.objects.create(log_type=Logging.INFO, message=message)
    
    def create_error(self, message):
        Logging.objects.create(log_type=Logging.ERROR, message=message)
        
class Logging(models.Model):
    INFO = 'INFO'
    ERROR = 'ERROR'
    LOG_TYPE = (
        (INFO, u'info'),
        (ERROR, u'error'),
        )    

    log_type = models.CharField(max_length=10, choices=LOG_TYPE, default=INFO)
    message = models.CharField(max_length=255, blank=True)
    registry = models.DateTimeField(auto_now_add=True)
    
    objects = LoggingManager()
    
    class Meta:
        ordering = ['registry']

    def __unicode__(self):
        return self.message

class KeywordManager(models.Manager):
    def search(self, term):
        return Keyword.objects.filter(Q(term__icontains=term))

    def add_or_update(self, term):
        obj, created = Keyword.objects.get_or_create(term=term)
        if not created:
            obj.count += 1
            obj.save()
        
class Keyword(models.Model):
    term = models.CharField(max_length=50)
    count = models.IntegerField(default=0)
    registry = models.DateTimeField(auto_now_add=True)
    
    objects = KeywordManager()
    
    class Meta:
        ordering = ['count']

    def __unicode__(self):
        return self.term
